﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel;
//using Windows.UI.Xaml;
//using Windows.UI.Xaml.Controls;
using Microsoft.Maui;
//using Microsoft.Maui.Controls;
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

namespace Caliburn.Micro.Maui
{
    /// <summary>
    /// Encapsulates the app and its available services.
    /// </summary>
    public abstract class CaliburnApplication : MauiWinUIApplication
    {
        private bool isInitialized;

        /// <summary>
        /// The root frame of the application.
        /// </summary>
        //protected Frame RootFrame { get; private set; }

        /// <summary>
        /// Called by the bootstrapper's constructor at design time to start the framework.
        /// </summary>
        protected virtual void StartDesignTime()
        {
            AssemblySource.Instance.Clear();
            AssemblySource.AddRange(SelectAssemblies());

            Configure();
        }

        /// <summary>
        /// Called by the bootstrapper's constructor at runtime to start the framework.
        /// </summary>
        protected virtual void StartRuntime()
        {
            AssemblySourceCache.Install();
            AssemblySource.AddRange(SelectAssemblies());

            PrepareApplication();
            Configure();

        }

        /// <summary>
        /// Start the framework.
        /// </summary>
        protected void Initialize()
        {
            if (isInitialized)
            {
                return;
            }

            isInitialized = true;

            PlatformProvider.Current = new MauiPlatformProvider();

            var baseExtractTypes = AssemblySourceCache.ExtractTypes;

            AssemblySourceCache.ExtractTypes = assembly =>
            {
                var baseTypes = baseExtractTypes(assembly);
                var elementTypes = assembly.GetExportedTypes()
                    .Where(t => typeof(UIElement).IsAssignableFrom(t));

                return baseTypes.Union(elementTypes);
            };

            AssemblySource.Instance.Refresh();


            if (Execute.InDesignMode)
            {
                try
                {
                    StartDesignTime();
                }
                catch
                {
                    //if something fails at design-time, there's really nothing we can do...
                    isInitialized = false;
                    throw;
                }
            }
            else
            {
                StartRuntime();
            }
        }

        ///// <summary>
        ///// Invoked when the application creates a window.
        ///// </summary>
        ///// <param name="args">Event data for the event.</param>
        //protected override void OnWindowCreated(Microsoft.UI.Xaml.WindowCreatedEventArgs args)
        //{
        //    base.OnWindowCreated(args);

        //    // Because dispatchers are tied to windows Execute will fail in 
        //    // scenarios when the app has multiple windows open (though contract 
        //    // activation, this keeps Excute up to date with the currently activated window

        //    args.Window.Activated += (s, e) => PlatformProvider.Current = new XamlPlatformProvider();
        //}

        /// <summary>
        /// Provides an opportunity to hook into the application object.
        /// </summary>
        protected virtual void PrepareApplication()
        {
            //Resuming += OnResuming;
            //Suspending += OnSuspending;
            UnhandledException += OnUnhandledException;
        }

        /// <summary>
        /// Override to configure the framework and setup your IoC container.
        /// </summary>
        protected virtual void Configure()
        {
        }

        /// <summary>
        /// Override to tell the framework where to find assemblies to inspect for views, etc.
        /// </summary>
        /// <returns>A list of assemblies to inspect.</returns>
        protected virtual IEnumerable<Assembly> SelectAssemblies()
        {
            return new[] {GetType().GetTypeInfo().Assembly};
        }


        /// <summary>
        /// Override this to add custom behavior when the application transitions from Suspended state to Running state.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event args.</param>
        protected virtual void OnResuming(object sender, object e)
        {
        }

        /// <summary>
        /// Override this to add custom behavior when the application transitions to Suspended state from some other state.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event args.</param>
        protected virtual void OnSuspending(object sender, SuspendingEventArgs e)
        {
        }

        /// <summary>
        /// Override this to add custom behavior for unhandled exceptions.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event args.</param>
        protected virtual void OnUnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
        {
        }

        ///// <summary>
        ///// Creates the root frame used by the application.
        ///// </summary>
        ///// <returns>The frame.</returns>
        //protected virtual Frame CreateApplicationFrame()
        //{
        //    return new Frame();
        //}

        ///// <summary>
        ///// Allows you to trigger the creation of the RootFrame from Configure if necessary.
        ///// </summary>
        //protected virtual void PrepareViewFirst()
        //{
        //    if (RootFrame != null)
        //        return;

        //    RootFrame = CreateApplicationFrame();
        //    PrepareViewFirst(RootFrame);
        //}

        ///// <summary>
        ///// Override this to register a navigation service.
        ///// </summary>
        ///// <param name="rootFrame">The root frame of the application.</param>
        //protected virtual void PrepareViewFirst(Frame rootFrame)
        //{
        //}

        ///// <summary>
        ///// Creates the root frame and navigates to the specified view.
        ///// </summary>
        ///// <param name="viewType">The view type to navigate to.</param>
        ///// <param name="paramter">The object parameter to pass to the target.</param>
        //protected void DisplayRootView(Type viewType, object paramter = null)
        //{
        //    Initialize();

        //    PrepareViewFirst();

        //    RootFrame.Navigate(viewType, paramter);

        //    // Seems stupid but observed weird behaviour when resetting the Content
        //    if (Window.Current.Content == null)
        //        Window.Current.Content = RootFrame;

        //    Window.Current.Activate();
        //}

        ///// <summary>
        ///// Creates the root frame and navigates to the specified view.
        ///// </summary>
        ///// <typeparam name="T">The view type to navigate to.</typeparam>
        ///// <param name="parameter">The object parameter to pass to the target.</param>
        //protected void DisplayRootView<T>(object parameter = null)
        //{
        //    DisplayRootView(typeof(T), parameter);
        //}

        ///// <summary>
        ///// Locates the view model, locates the associate view, binds them and shows it as the root view.
        ///// </summary>
        ///// <param name="viewModelType">The view model type.</param>
        ///// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
        ///// <returns>A task that represents the asynchronous operation.</returns>
        //protected async Task DisplayRootViewForAsync(Type viewModelType, CancellationToken cancellationToken)
        //{
        //    Initialize();

        //    var viewModel = IoC.GetInstance(viewModelType, null);
        //    var view = ViewLocator.LocateForModel(viewModel, null, null);

        //    ViewModelBinder.Bind(viewModel, view, null);

        //    if (viewModel is IActivate activator)
        //        await activator.ActivateAsync(cancellationToken);

        //    Window.Current.Content = view;
        //    Window.Current.Activate();
        //}

        ///// <summary>
        ///// Locates the view model, locates the associate view, binds them and shows it as the root view.
        ///// </summary>
        ///// <param name="viewModelType">The view model type.</param>
        ///// <returns>A task that represents the asynchronous operation.</returns>
        //protected Task DisplayRootViewForAsync(Type viewModelType) => DisplayRootViewForAsync(viewModelType, CancellationToken.None);

        ///// <summary>
        ///// Locates the view model, locates the associate view, binds them and shows it as the root view.
        ///// </summary>
        ///// <typeparam name="T">The view model type.</typeparam>
        ///// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
        ///// <returns>A task that represents the asynchronous operation.</returns>
        //protected Task DisplayRootViewForAsync<T>(CancellationToken cancellationToken)
        //{
        //    return DisplayRootViewForAsync(typeof(T), cancellationToken);
        //}

        ///// <summary>
        ///// Locates the view model, locates the associate view, binds them and shows it as the root view.
        ///// </summary>
        ///// <typeparam name="T">The view model type.</typeparam>
        ///// <returns>A task that represents the asynchronous operation.</returns>
        //protected Task DisplayRootViewForAsync<T>() => DisplayRootViewForAsync<T>(CancellationToken.None);
    }
}
